﻿using System;
using System.Threading;

#if WINDOWS_PHONE
namespace Fibre.WP7.Threading
#else 
#if SILVERLIGHT
namespace Fibre.Silverlight.Threading
#else
namespace Fibre.Threading
#endif
#endif
{
    public sealed class AsyncResult<T> : IAsyncResult, IDisposable
    {
        private readonly AsyncCallback _callback;
        private bool _completed;
        private bool _completedSynchronously;
        private readonly object _asyncState;
        private ManualResetEvent _waitHandle;
        private T _result;
        private Exception _error;
        private readonly object _syncRoot = new object();

        public AsyncResult(AsyncCallback cb, object state)
            : this(cb, state, false)
        { }

        private AsyncResult(AsyncCallback cb, object state, bool completed)
        {
            _callback = cb;
            _asyncState = state;
            _completed = completed;
            _completedSynchronously = completed;
        }

        public object AsyncState
        {
            get { return _asyncState; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                lock (_syncRoot)
                {
                    return _waitHandle ?? (_waitHandle = new ManualResetEvent(false));
                }
            }
        }

        public bool CompletedSynchronously
        {
            get
            {
                lock (_syncRoot)
                {
                    return _completedSynchronously;
                }
            }
        }

        public bool IsCompleted
        {
            get
            {
                lock (_syncRoot)
                {
                    return _completed;
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!disposing) return;
            lock (_syncRoot)
            {
                if (_waitHandle != null)
                {
                    ((IDisposable)_waitHandle).Dispose();
                }
            }
        }

        public Exception Exception
        {
            get
            {
                lock (_syncRoot)
                {
                    return _error;
                }
            }
        }

        public T Result
        {
            get
            {
                lock (_syncRoot)
                {
                    return _result;
                }
            }
        }

        public void Complete(T result, bool completedSynchronously)
        {
            lock (_syncRoot)
            {
                _completed = true;
                _completedSynchronously =
                    completedSynchronously;
                _result = result;
            }

            SignalCompletion();
        }

        public void HandleException(Exception e, bool completedSynchronously)
        {
            lock (_syncRoot)
            {
                _completed = true;
                _completedSynchronously = completedSynchronously;
                _error = e;
            }

            SignalCompletion();
        }

        private void SignalCompletion()
        {
            //Only need to signal WaitHandle if it has been used
            if (AsyncWaitHandle != null)
                ((ManualResetEvent)AsyncWaitHandle).Set();
            if (_callback != null)
            {
                _callback(this);
            }
        }
    }
}